﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "CheckCom.h"
#include <QSettings>
#include <QMessageBox>
#include "BurnToolCommon/MyThread.h"
#include "BurnToolCommon/Global.h"
#include "SecureLink/Channel.h"
#include "../platform/Securec/securec.h"
#include "DataConvertor.h"
using namespace BurnDlg;
namespace {
    const int CACHING_TWO_THOUSAND = 20000;
    const int DEFAULT_BAUD_RATE = 115200;
    const int DEFAULT_CTRL_PERIOD = 300;
    const int DEFAULT_DATABITS = 8;
    const int NUM_THREE = 3;
    const int TIME_INTERVAL_1000 = 1000;
}

CheckCom::CheckCom(QObject *pParent)
    : QObject(pParent),
      witeThreadAlive(false),
      witeThreadMsgId(WM_COMM_RXCHAR_WITE_THREAD),
      comIndex(0),
      isApplyResult(false),
      strResult(""),
      fsm(CheckComFsm::CHECKCOM_FSM_NONE),
      ctrlPeriod(DEFAULT_CTRL_PERIOD),
      baud (DEFAULT_BAUD_RATE),
      comNum(-1),
      cParity(QSerialPort::NoParity),
      databits(DEFAULT_DATABITS),
      stopbits(0),
      flowctrl(0),
      sendCtrlTimer(nullptr),
      sendCtrlTimerEvent(0)
{
    outputPosition.clear();
    outputCom.clear();
}


CheckCom::~CheckCom()
{
}

void CheckCom::StartCheck()
{
    if (fsm != CheckComFsm::CHECKCOM_FSM_NONE) {
        return;
    }
    QString strEfuseName = "CHECK_COM_TIME";
    QString path = DataConvertor::GetBurnToolConfigPathNew();
    QSettings cfg(path, QSettings::IniFormat);
    QString ascReturnedString1 = cfg.value(DLG_CONFIG + "/" + strEfuseName).toString();
    if (!ascReturnedString1.isEmpty()) {
        QString strName = ascReturnedString1;
        ctrlPeriod = DataConvertor::Str2U32DecHex(strName);
    } else {
        cfg.setValue(DLG_CONFIG + "/" + strEfuseName, "300");
    }

    isApplyResult = false;
    outputCom.clear();
    outputPosition.clear();
    comIndex = 0;
    RefreshComList(comList);
    if (comList.size() == 0) {
        QMessageBox::warning(nullptr, "", stringClass.GetIdsString32899());
        return;
    }
    if (memset_s(dataArr, MAX_BUFFER_LEN, 0, MAX_BUFFER_LEN) != 0) {
        return;
    }

    fsm = CheckComFsm::CHECKCOM_FSM_DOING;
    comNum = comList[comIndex].mid(NUM_THREE).toInt();
    ConfigComPort(comNum);

    if (sendCtrlTimer == nullptr) {
        sendCtrlTimer = new QTimer();
    }
    if (sendCtrlTimer == nullptr) {
        return;
    }
    if (sendCtrlTimer == nullptr) {
        return;
    }
    sendCtrlTimerEvent = TIMER_ID_CHECK_COM_TIME;
    sendCtrlTimer->setInterval(TIME_INTERVAL_1000);
    sendCtrlTimer->setTimerType(Qt::PreciseTimer);
    sendCtrlTimer->setSingleShot(true);
    sendCtrlTimer->start();
    connect(sendCtrlTimer, &QTimer::timeout, this, &CheckCom::SlotBurnCtrlCommonTimerHandler, Qt::DirectConnection);
}

void CheckCom::RefreshComList(QStringList &result) const
{
    result.clear();
    HKEY hKey = nullptr;
    int ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, IDS_STRING_33109.toStdWString().c_str(), 0, KEY_READ, &hKey);
    if (ret == ERROR_SUCCESS) {
        int i = 0;
        TCHAR portName[256]; // 256 name max len
        TCHAR commName[256]; // 256 name max len
        DWORD dwLong;
        DWORD dwSize;
        while (i < INT_MAX) {
            dwLong = dwSize = sizeof(portName);
            if (::RegEnumValue(hKey, i, portName, &dwLong, nullptr, nullptr,
                reinterpret_cast<PUCHAR>(commName), &dwSize) == ERROR_NO_MORE_ITEMS) {
                break;
            }
            QString strtmp = QString::fromWCharArray(commName);
            result.push_back(strtmp);
            i++;
        }
        RegCloseKey(hKey);
    }
}

bool CheckCom::ConfigComPort(int com)
{
    PortBaseInitStru serialPortInit;
    serialPortInit.pPortOwner = nullptr;
    serialPortInit.portnr = com;
    serialPortInit.baud = baud;
    serialPortInit.parity = cParity;
    serialPortInit.databits = databits;
    serialPortInit.stopbits = stopbits;
    serialPortInit.flowctrl = flowctrl;
    serialPortInit.dwCommEvents = EV_RXFLAG | EV_RXCHAR | EV_CTS;
    serialPortInit.writebuffersize = CACHING_TWO_THOUSAND;
    serialPortInit.witeThreadMsgId = WM_COMM_RXCHAR_WITE_THREAD;
    serialPortInit.witeThread = static_cast<MyThreadClass::MyThread*>(this->thread());
    serialPortInit.oneTimeReadByte = 30; // 30bytes
    serialPortInit.isForceReadWithSleep = false;
    serialPortInit.forceReadTime = 10; // 10 times
    if (portManager.InitPort(serialPortInit)) {
        portManager.StartMonitoring();
        return true;
    } else {
        QMessageBox::warning(nullptr, "", stringClass.GetIdsString32812().arg(com));
        return false;
    }
}

UINT CheckCom::CommWiteThread(unsigned int msgId, WPARAM srcData, LPARAM srcLen)
{
    if (msgId == witeThreadMsgId) {
        SlotCommunication(srcData, srcLen);
    } else if (msgId == static_cast<unsigned int>(CheckComWiteThreadMsg::WITE_THREAD_MSG_STARTCHECKCOM)) {
        StartCheck();
    }
    return 0;
}

LONG CheckCom::SlotCommunication(WPARAM srcData, LPARAM srcLen)
{
    PBYTE data = reinterpret_cast<PBYTE>(srcData);
    unsigned int len = static_cast<unsigned int>(srcLen);
    if (len > MAX_BUFFER_LEN) {
        len = MAX_BUFFER_LEN;
    }

    if (memcpy_s(dataArr + u32dataLen, MAX_BUFFER_LEN - u32dataLen, data, len) != 0) {
        delete[] data;
        return 0;
    }
    u32dataLen += len;

    ProcessReceiveData(dataArr, u32dataLen);

    delete[] data;
    return 0;
}

void CheckCom::ProcessReceiveData(PBYTE data, unsigned int &dataLen)
{
    if (data == nullptr || dataLen > MAX_BUFFER_LEN) {
        return;
    }
    if (fsm != CheckComFsm::CHECKCOM_FSM_DOING) {
        return;
    }
    SEBOOT_ACK_FRAME chlPktHead;
    int resDataLen = dataLen;
    int decodeResult = ChannelClass::Channel::DecodeSEBootAck(data, dataLen, chlPktHead, resDataLen);
    if (decodeResult == DECODE_PACKET_SUCCSS || decodeResult == DECODE_PACKET_MORE_THAN_ONE) {
        int position = chlPktHead.result;
        QString key = IDS_STRING_33185 + QString::number(position);
        QString strValue = QString::number(comNum);
        outputPosition.push_back(key);
        outputCom.push_back(strValue);
        NextCheck();
    }
}

void CheckCom::NextCheck()
{
    KillMyTimer();
    comIndex++;
    portManager.ClosePort();
    if (comIndex >= comList.size()) {
        return;
    }
}

void CheckCom::KillMyTimer()
{
    if (sendCtrlTimer != nullptr) {
        sendCtrlTimer->stop();
        delete sendCtrlTimer;
        sendCtrlTimer = nullptr;
    }
}

void CheckCom::SaveResult() const
{
    QString path = DataConvertor::GetBurnToolConfigPathNew();
    QSettings cfg(path, QSettings::IniFormat);
    for (unsigned int i = 0; i < EFUSE_ITEM_MAX; ++i) {
        QString strEfuseName = IDS_STRING_33185 + QString::number(1 + i);
        cfg.setValue("com_disposition/" + strEfuseName, "");
    }
    for (int i = 0; i < outputCom.size(); i++) {
        cfg.setValue("com_disposition/" + outputPosition[i], outputCom[i]);
    }
}

bool CheckCom::IsBusy() const
{
    return fsm == CheckComFsm::CHECKCOM_FSM_DOING;
}

bool CheckCom::IsApplyResult() const
{
    return isApplyResult;
}

void CheckCom::SetApplyResult(bool result)
{
    isApplyResult = result;
}

QString CheckCom::GetStrResult()
{
    return strResult;
}

void CheckCom::SlotBurnCtrlCommonTimerHandler()
{
    switch (sendCtrlTimerEvent) {
        case TIMER_ID_CHECK_COM_TIME:
            NextCheck();
            break;
        default:
            return;
    }
}
